package com.speedcar.base.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.speedcar.base.annotation.SpeedCarLog;
import com.speedcar.base.exception.SpeedCarBaseException;
import com.speedcar.base.i18n.ScI18nUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author ZhangFZ
 * 配置切面类，@Component 注解把切面类放入Ioc容器中
 */
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class ScLogAspect {

    @Pointcut("(execution(public * com.speedcar..controller.*.*(..)) || " +
            "execution(public * com.oldcar..controller.*.*(..)) || " +
            "execution(public * com.cyl..controller.*.*(..))) " +
            "&& !within(com.oldcar..controller.BaseController)")
    public void allControllerMethods() {}

    @Pointcut("@annotation(com.speedcar.base.annotation.SpeedCarLog)")
    public void speedCarLogAnnotatedMethods() {}

    @Pointcut("allControllerMethods() || speedCarLogAnnotatedMethods()")
    public void applicationLogPointcut() {}

    @Around(value = "applicationLogPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj;
        // 定义执行开始时间
        long startTime;
        // 定义执行结束时间
        long endTime;
        HttpServletRequest request = null;
        if (Objects.nonNull(RequestContextHolder.getRequestAttributes())) {
            request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        }

        // 处理参数
        String operateParam = "";
        // 当序列化失败时，采用更安全的方式处理
        Object[] args = joinPoint.getArgs();
        try {
            if (args != null) {
                for (Object arg : args) {
                    if (arg instanceof java.io.File
                            || arg instanceof java.io.InputStream
                            || arg instanceof MultipartFile) {
                        operateParam = "文件";
                    } else {
                        operateParam = JSON.toJSONStringWithDateFormat(joinPoint.getArgs(), "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue);
                    }
                }
            }
        } catch (Exception e) {
            StringBuilder sb = new StringBuilder();
            sb.append("[");
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof java.io.File
                        || args[i] instanceof java.io.InputStream
                        || args[i] instanceof MultipartFile) {
                    continue; // 如果是文件，不打印该参数
                }
                if (i > 0) sb.append(", ");
                try {
                    // 尝试单独序列化每个参数
                    String argStr = JSON.toJSONStringWithDateFormat(args[i], "yyyy-MM-dd HH:mm:ss", SerializerFeature.WriteMapNullValue);
                    sb.append(argStr);
                } catch (Exception ex) {
                    // 单个参数序列化失败时，使用 toString 或简单描述
                    if (args[i] != null) {
                        String className = args[i].getClass().getSimpleName();
                        sb.append(className).append("@").append(Integer.toHexString(args[i].hashCode()));
                    } else {
                        sb.append("null");
                    }
                }
            }
            sb.append("]");
            operateParam = sb.toString();
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SpeedCarLog speedCarLog = method.getAnnotation(SpeedCarLog.class);
        String desc = "";
        boolean isPrintParam = true;
        boolean isPrintResult = true;
        boolean isSimpleResult = false;

        if (speedCarLog != null) {
            desc = StringUtils.isNotEmpty(speedCarLog.value()) ? speedCarLog.value() : "";
            isPrintParam = speedCarLog.isPrintParam();
            isPrintResult = speedCarLog.isPrintResult();
            isSimpleResult = speedCarLog.isSimpleResult();
        }
        startTime = System.currentTimeMillis();

        try {
            log.info("{} 请求开始, 请求方法: {}#{}, 请求参数:{}, 请求IP:{}",
                    desc,
                    joinPoint.getTarget().getClass().getSimpleName(),
                    joinPoint.getSignature().getName(),
                    isPrintParam ? operateParam : "日志忽略了打印请求参数",
                    getIpAddress(request));
            obj = joinPoint.proceed();
            endTime = System.currentTimeMillis();
            String result = JSON.toJSONString(obj);
            if (isSimpleResult && result.length() > 201) {
                result = result.substring(0, 200);
            }
            log.info("{} 请求完成, 返回参数: {}, 耗时:{} ms",
                    desc,
                    isPrintResult ? result : "日志忽略了打印返回参数",
                    endTime - startTime);
        } catch (SpeedCarBaseException e) {
            log.error("{} 请求异常, 异常code：{}, 异常信息：{}", desc, e.getMessage(), ScI18nUtils.get(e.getMessage(), e.getArgs()));
            throw e;
        } catch (Exception e) {
            log.error("{} 请求异常, 异常code：{}", desc, e.getMessage());
            throw e;
        }
        return obj;
    }

    /**
     * 获取Ip地址
     */
    private static String getIpAddress(HttpServletRequest request) {
        if (Objects.isNull(request)) {
            return "";
        }
        String xip = request.getHeader("X-Real-IP");
        String xFor = request.getHeader("X-Forwarded-For");
        String unknown = "unknown";
        if (StringUtils.isNotEmpty(xFor) && !unknown.equalsIgnoreCase(xFor)) {
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = xFor.indexOf(",");
            if (index != -1) {
                return xFor.substring(0, index);
            } else {
                return xFor;
            }
        }
        xFor = xip;
        if (StringUtils.isNotEmpty(xFor) && !unknown.equalsIgnoreCase(xFor)) {
            return xFor;
        }
        if (StringUtils.isBlank(xFor) || unknown.equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || unknown.equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(xFor) || unknown.equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(xFor) || unknown.equalsIgnoreCase(xFor)) {
            xFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(xFor) || unknown.equalsIgnoreCase(xFor)) {
            xFor = request.getRemoteAddr();
        }
        return xFor;
    }
}
